/*
Copyright SecureKey Technologies Inc. All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
package resmgmt

import (
	"git.cloud.inspur.com/ichain/ichain-sdk-go/pkg/providers/ichain"
	"git.cloud.inspur.com/ichain/ichain-sdk-go/thirdparty/ichain/protoutil"
	"github.com/golang/protobuf/proto"
	"github.com/pkg/errors"

	"git.cloud.inspur.com/ichain/ichain-protos-go/common"
	pb "git.cloud.inspur.com/ichain/ichain-protos-go/inode"
)

const (
	lscc        = "SC_LIFECYCLE"
	lsccDeploy  = "deploy"
	lsccUpgrade = "upgrade"
	escc        = "sc_endorse"
	vscc        = "sc_verify"
)

// contractProposalType  合约提案类型：实例化合约、更新合约
type contractProposalType int

// Define contract proposal types
const (
	InstantiateContract contractProposalType = iota
	UpgradeContract
)

// contractDeployRequest 合约请求，实例化或者更新合约
type contractDeployRequest struct {
	Name       string
	Path       string
	Version    string
	Lang       pb.ContractSpec_Type
	Args       [][]byte
	Policy     *common.SignaturePolicyEnvelope
	CollConfig []*common.CollectionConfig
}

// createContractDeployProposal 创建一个合约部署提案
func createContractDeployProposal(tx ichain.Transactor, txh ichain.TransactionHeader, deploy contractProposalType, chainID string, contract contractDeployRequest) (*ichain.TransactionProposal, error) {

	args := [][]byte{}
	args = append(args, []byte(chainID))

	ccds := &pb.ContractDeploymentSpec{ContractSpec: &pb.ContractSpec{
		Type: pb.ContractSpec_GOLANG, ContractId: &pb.ContractID{Name: contract.Name, Path: contract.Path, Version: contract.Version},
		Input: &pb.ContractInput{Args: contract.Args}}}
	ccdsBytes, err := protoutil.Marshal(ccds)
	if err != nil {
		return nil, errors.WithMessage(err, "marshal of contract deployment spec failed")
	}
	args = append(args, ccdsBytes)

	contractPolicyBytes, err := protoutil.Marshal(contract.Policy)
	if err != nil {
		return nil, errors.WithMessage(err, "marshal of contract policy failed")
	}
	args = append(args, contractPolicyBytes)

	// sc_endorse sc_verify 背书和验证
	args = append(args, []byte(escc))
	args = append(args, []byte(vscc))

	if contract.CollConfig != nil {
		collConfigBytes, err := proto.Marshal(&common.CollectionConfigPackage{Config: contract.CollConfig})
		if err != nil {
			return nil, errors.WithMessage(err, "marshal of collection policy failed")
		}
		args = append(args, collConfigBytes)
	}

	fcn := ""
	switch deploy {
	case InstantiateContract:
		fcn = lsccDeploy // deploy
	case UpgradeContract:
		fcn = lsccUpgrade // upgrade
	default:
		return nil, errors.New("contract deployment type unknown")
	}

	cir := ichain.ContractInvokeRequest{
		ContractID: lscc, // SC_LIFECYCLE
		Lang:       contract.Lang,
		Fcn:        fcn,
		Args:       args,
	}
	return tx.CreateTxProposalWithHeader(txh, cir)
}
